﻿#region Copyright 2011 Mike Goatly
// This source is subject to the the MIT License (MIT)
// All rights reserved.
#endregion

namespace Lifti.Tests.Persistence.PageManagerTests.Transactional
{
    using System.Transactions;

    using Lifti.Persistence;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using Moq;

    /// <summary>
    /// Tests for saving a page using the <see cref="TransactionalPageManager&lt;TItem&gt;"/> class.
    /// </summary>
    [TestClass]
    public class SavingPage : TransactionalPageManagerTestBase
    {
        /// <summary>
        /// If when a page is saved it contains items, only that page should be put into the
        /// transaction cache.
        /// </summary>
        [TestMethod]
        public void SavingPopulatedPageShouldForceTheEntirePageIntoTransactionPageCache()
        {
            using (var scope = new TransactionScope())
            {
                var page = this.PageManager.GetPage(0);
                this.TransactionLog.Verify(l => l.RegisterAffectedPage(page, PageWriteLevels.Body), Times.Never());

                this.PageManager.SavePage(page);

                this.TransactionLog.Verify(l => l.RegisterAffectedPage(page, PageWriteLevels.Body), Times.Exactly(1));
                this.TransactionLog.VerifySet(l => l.PageManagerHeaderWritten = true, Times.Never());
            }        
        }

        /// <summary>
        /// If when a page is saved and it only affects the information in its header, then
        /// just that should be registered against the transaction.
        /// </summary>
        [TestMethod]
        public void AffectingAPageHeaderShouldCauseJustThatToBeRegisteredAgainstTransaction()
        {
            using (var scope = new TransactionScope())
            {
                var page = (IndexNodeDataPage)this.PageManager.GetPage(2);

                // Remove all entries and save the page to force its invalidation. This will affect the page links around it
                page.RemoveEntry(e => true);
                this.PageManager.SavePage(page);

                this.TransactionLog.Verify(l => l.RegisterAffectedPage(It.Is<IDataPage>(p => p.Header.PageNumber == 0), PageWriteLevels.Header), Times.Exactly(1));
                this.TransactionLog.Verify(l => l.RegisterAffectedPage(page, PageWriteLevels.Header), Times.Exactly(1));
                this.TransactionLog.Verify(l => l.RegisterAffectedPage(It.Is<IDataPage>(p => p.Header.PageNumber == 3), PageWriteLevels.Header), Times.Exactly(1));
                this.TransactionLog.VerifySet(l => l.PageManagerHeaderWritten = true, Times.Never());
            }
        }

        /// <summary>
        /// If when a page is saved and it affects the actual pag.
        /// </summary>
        [TestMethod]
        public void AffectingThePageManagerHeaderViaSavingAPageShouldCauseTheWrittenFlagToBeSet()
        {
            using (var scope = new TransactionScope())
            {
                var page = (IndexNodeDataPage)this.PageManager.GetPage(0);

                // Remove all entries and save the page to force its invalidation. This will affect the page links around it
                // (and in this case, because it was the first page, the page manager header too)
                page.RemoveEntry(e => true);
                this.PageManager.SavePage(page);
            }

            this.TransactionLog.Verify(l => l.RegisterAffectedPage(It.Is<IDataPage>(p => p.Header.PageNumber == 0), PageWriteLevels.Header), Times.Exactly(1));
            this.TransactionLog.Verify(l => l.RegisterAffectedPage(It.Is<IDataPage>(p => p.Header.PageNumber == 2), PageWriteLevels.Header), Times.Exactly(1));
            this.TransactionLog.Verify(l => l.RegisterAffectedPage(It.Is<IDataPage>(p => p.Header.PageNumber == 3), PageWriteLevels.Header), Times.Never());
            this.TransactionLog.VerifySet(l => l.PageManagerHeaderWritten = true, Times.Exactly(1));
        }

        /// <summary>
        /// A page saved during a transaction should not be immediately written to the backing store.
        /// </summary>
        [TestMethod]
        public void SavingATransactionalPageShouldNotImmediatelyWriteToBackingStore()
        {
            using (var scope = new TransactionScope())
            {
                var page = (IndexNodeDataPage)this.PageManager.GetPage(0);

                // Remove all entries and save the page to force its invalidation. This will affect the page links around it
                // (and in this case, because it was the first page, the page manager header too)
                page.RemoveEntry(e => true);
                this.PageManager.SavePage(page);
            }

            this.DataFileManager.Mock.Verify(w => w.GetDataWriter(It.IsAny<int>(), It.IsAny<int>()), Times.Never());
        }
    }
}
